package com.ruoyi.framework.aspectj;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;

/**
 * 内存限流处理 - 替代Redis限流
 *
 * @author ruoyi
 */
@Aspect
@Component
public class MemoryRateLimiterAspect
{
    private static final Logger log = LoggerFactory.getLogger(MemoryRateLimiterAspect.class);
    
    // 存储限流信息的Map，key为限流键，value为限流计数器
    private final ConcurrentHashMap<String, RateLimitInfo> rateLimitMap = new ConcurrentHashMap<>();
    
    /**
     * 限流信息
     */
    private static class RateLimitInfo {
        private final AtomicInteger count = new AtomicInteger(0);
        private final AtomicLong resetTime = new AtomicLong(0);
        
        public boolean isExpired() {
            return System.currentTimeMillis() > resetTime.get();
        }
        
        public void reset(int time) {
            count.set(0);
            resetTime.set(System.currentTimeMillis() + time * 1000);
        }
        
        public int incrementAndGet() {
            return count.incrementAndGet();
        }
        
        public int getCount() {
            return count.get();
        }
    }

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
    {
        int time = rateLimiter.time();
        int count = rateLimiter.count();

        String combineKey = getCombineKey(rateLimiter, point);
        
        try
        {
            RateLimitInfo rateLimitInfo = rateLimitMap.computeIfAbsent(combineKey, k -> new RateLimitInfo());
            
            // 如果限流信息过期，重置
            if (rateLimitInfo.isExpired()) {
                rateLimitInfo.reset(time);
            }
            
            int currentCount = rateLimitInfo.incrementAndGet();
            
            if (currentCount > count)
            {
                throw new ServiceException("访问过于频繁，请稍候再试");
            }
            log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, currentCount, combineKey);
        }
        catch (ServiceException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new RuntimeException("服务器限流异常，请稍候再试");
        }
    }

    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
    {
        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP)
        {
            stringBuffer.append(IpUtils.getIpAddr()).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuffer.toString();
    }
}
